package com.project.base.common;

import com.alibaba.fastjson.JSONObject;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilder;

import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

/**
 * @author dengcheng
 * @version V1.0.0
 * @Title: ElasticUtils.java
 * @Package com.lvtu.monitor.utils
 * @date 2015-5-15 下午1:41:51
 */
public class ElasticUtils {
    private static volatile ElasticUtils instance;
    private TransportClient client;
    public static final String HIGHLIGHTER_PRE_TAGS = "<span style=\"color:red;\">";
    public static final String HIGHLIGHTER_POST_TAGS = "</span>";

    public ElasticUtils() {
        init();
    }

    public void init() {
        // 配置你的es,现在这里只配置了集群的名,默认是elasticsearch,跟服务器的相同
        // 这里可以同时连接集群的服务器,可以多个,并且连接服务是可访问的
        String elasticClusterName = Constants.getInstance().getValue("elastic.cluster.name");
        Settings settings = Settings.settingsBuilder().put("cluster.name", elasticClusterName).build();
        client = TransportClient.builder().settings(settings).build();

        String[] elasticUrls = Constants.getInstance().getValue("elastic.url").split(",");
        for (String elasticUrl : elasticUrls) {
            String elasticHost = elasticUrl.split(":")[0];
            int elasticPort = Integer.valueOf(elasticUrl.split(":")[1]);
            try {
                client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(elasticHost), elasticPort));
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
        }
    }

    public class ElasticSearchResult<T> implements Serializable {
        private static final long serialVersionUID = -4434715473675532823L;
        private T data;
        private String[] highlightTexts;

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }

        public String[] getHighlightTexts() {
            return highlightTexts;
        }

        public void setHighlightTexts(String[] highlightTexts) {
            this.highlightTexts = highlightTexts;
        }
    }

    public class ElasticPageResult<T> {
        private int page;
        private int pageSize;
        private long totalSize;
        private List<T> list;

        public int getPage() {
            return page;
        }

        public void setPage(int page) {
            this.page = page;
        }

        public int getPageSize() {
            return pageSize;
        }

        public void setPageSize(int pageSize) {
            this.pageSize = pageSize;
        }

        public long getTotalSize() {
            return totalSize;
        }

        public void setTotalSize(long totalSize) {
            this.totalSize = totalSize;
        }

        public List<T> getList() {
            return list;
        }

        public void setList(List<T> list) {
            this.list = list;
        }
    }

    public static ElasticUtils getInstance() {
        if (instance == null) {
            synchronized (ElasticUtils.class) {
                if (instance == null) {
                    instance = new ElasticUtils();
                }
            }
        }
        return instance;
    }

    /**
     * 以json方式提交索引
     *
     * @param index 索引库名称
     * @param type  索引类型
     * @param json  json格式数据
     * @throws InterruptedException
     * @throws ExecutionException
     * @author dengcheng
     * @date 2015-5-15 下午1:59:54
     */
    public void putIndex(String index, String type, String json) throws InterruptedException, ExecutionException {
        UUIDGenerator gen = new UUIDGenerator();
        putIndex(index, type, gen.generate().toString(), json);
    }

    /**
     * 以json方式提交索引
     *
     * @param index 索引库名称
     * @param type  索引类型
     * @param id    索引文档ID
     * @param json  json格式数据
     * @throws InterruptedException
     * @throws ExecutionException
     * @author dengcheng
     * @date 2015-5-15 下午1:59:54
     */
    public void putIndex(String index, String type, String id, String json) throws InterruptedException, ExecutionException {
        IndexRequest indexRequest = new IndexRequest(index, type, id).source(json);
        client.index(indexRequest).get();
    }

    /**
     * 统计匹配的索引个数
     *
     * @param index  索引库名称
     * @param query  查询条件
     * @param filter 筛选条件
     * @return
     */
    public <T> Long countIndex(String index, QueryBuilder query, QueryBuilder filter) {
        SearchResponse response = client.prepareSearch(index).setFrom(0)
                .setQuery(query).setPostFilter(filter)
                .setSearchType(SearchType.COUNT).execute().actionGet();
        long hitNum = response.getHits().getTotalHits();
        return hitNum;
    }

    public <T> ElasticPageResult<ElasticSearchResult<T>> queryIndexAndHighlighted(String index, Class<T> clazz, QueryBuilder query, QueryBuilder filter, SortBuilder sort, int start, int size, String[] highlighteds) {
        SearchResponse response = query(index, query, filter, sort, start, size, highlighteds);

        ElasticPageResult<ElasticSearchResult<T>> result = new ElasticPageResult<ElasticSearchResult<T>>();
        List<ElasticSearchResult<T>> resultList = new ArrayList<ElasticSearchResult<T>>();
        result.setList(resultList);

        for (SearchHit hit : response.getHits().getHits()) {
            ElasticSearchResult<T> tempObj = new ElasticSearchResult<T>();
            resultList.add(tempObj);

            String value = hit.getSourceAsString();
            T data = JSONObject.parseObject(value, clazz);
            tempObj.setData(data);
            // 获取对应的高亮域
            Map<String, HighlightField> highLightResult = hit.highlightFields();
            if (highlighteds == null) {
                continue;
            }
            for (String highlighted : highlighteds) {
                // 从设定的高亮域中取得指定域
                HighlightField highlightField = highLightResult.get(highlighted);
                if (highlightField == null) {
                    continue;
                }
                // 取得定义的高亮标签
                Text[] highlightTexts = highlightField.fragments();
                String[] highlight = new String[highlightTexts.length];
                for (int i = 0; i < highlightTexts.length; i++) {
                    highlight[i] = highlightTexts[i].toString();
                }
                tempObj.setHighlightTexts(highlight);
            }
        }

        result.setTotalSize(response.getHits().getTotalHits());
        return result;
    }

    /**
     * 批量获取索引值
     *
     * @param index  索引库名称
     * @param clazz  匹配的对象
     * @param query  查询条件
     * @param filter 筛选条件
     * @param sort   排序条件
     * @param start  开始位置
     * @param size   获取条数
     * @return
     */
    public <T> ElasticPageResult<T> queryIndex(String index, Class<T> clazz, QueryBuilder query, QueryBuilder filter, SortBuilder sort, int start, int size) {
        ElasticPageResult<ElasticSearchResult<T>> queryResult = queryIndexAndHighlighted(index, clazz, query, filter, sort, start, size, null);
        List<ElasticSearchResult<T>> resultList = queryResult.getList();
        List<T> list = new ArrayList<T>();
        for (ElasticSearchResult<T> temp : resultList) {
            list.add(temp.getData());
        }
        ElasticPageResult<T> result = new ElasticPageResult<T>();
        result.setList(list);
        result.setPageSize(queryResult.getPageSize());
        return result;
    }

    private SearchResponse query(String index, QueryBuilder query, QueryBuilder filter, SortBuilder sort, int start, int size, String[] highlighteds) {
        SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index).setFrom(start).setQuery(query).setPostFilter(filter).setSize(size).setExplain(true).addSort(sort);
        if (highlighteds != null && highlighteds.length > 0) {
            for (String highlighted : highlighteds) {
                searchRequestBuilder.addHighlightedField(highlighted);
            }
            searchRequestBuilder.setHighlighterPreTags(HIGHLIGHTER_PRE_TAGS);
            searchRequestBuilder.setHighlighterPostTags(HIGHLIGHTER_POST_TAGS);
        }
        SearchResponse response = searchRequestBuilder.execute().actionGet();
        return response;
    }

    /**
     * @param params  查询参数
     * @param tpName  定义的模板名称
     * @param clazz   返回对象clazz
     * @param sources 模板源码
     * @Description: TODO
     * @author dengcheng
     * @date 2015-6-11 上午11:40:01
     */
    public <T> List<T> queryByTemplate(Map<String, Object> params, String tpName, Class<T> clazz, String sources) {
        client.preparePutIndexedScript("mustache", tpName, sources).get();
        SearchResponse response = client.prepareSearch()
                .setTemplateName(tpName)
                .setTemplateType(ScriptService.ScriptType.INDEXED)
                .setTemplateParams(params).setFrom(0).setSize(100).setExplain(true)
                .get();

        List<T> exlist = new ArrayList<T>();
        for (SearchHit hit : response.getHits().getHits()) {
            String value = hit.getSourceAsString();
            exlist.add(JSONObject.parseObject(value, clazz));
        }
        return exlist;
    }

}
